package com.buddy.qreport.query.service.impl;

import com.buddy.qreport.common.mybatis.entity.Page;
import com.buddy.qreport.datasource.dynamic.DataSourceContextHolder;
import com.buddy.qreport.datasource.service.IDataSourceService;
import com.buddy.qreport.query.dao.IQueryMapper;
import com.buddy.qreport.query.entity.QueryEntity;
import com.buddy.qreport.query.params.ParamDataSourceType;
import com.buddy.qreport.query.params.ParamDataType;
import com.buddy.qreport.query.params.QueryParam;
import com.buddy.qreport.query.service.IQueryService;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.sql.SQLException;
import java.util.*;

@Slf4j
@Service("QREPORT_QueryService")
public class QueryServiceImpl implements IQueryService {

    @Autowired
    private IQueryMapper queryMapper;

    @Autowired
    @Qualifier("QREPORT_DataSourceService")
    private IDataSourceService dataSourceService;

    @Override
    public List<Map> testSql(String datasourceId, String sql) throws SQLException {


        DataSourceContextHolder.setDataSourceId(datasourceId);

        List<Map> result = queryMapper.selectDynamicSql(sql);

        DataSourceContextHolder.clearId();

        return result;
    }


    @Transactional
    @Override
    public void insert(QueryEntity queryEntity) {

        queryMapper.insert(queryEntity);

        if (queryEntity.getQueryId() != null) {

            if (queryEntity.getQuerParamsConfig() != null && !queryEntity.getQuerParamsConfig().isEmpty()) {

                queryMapper.insertParamsBatch(queryEntity.getQuerParamsConfig(), queryEntity.getQueryId());

            }

            if (queryEntity.getQueryResultConfig() != null && !queryEntity.getQueryResultConfig().isEmpty()) {

                queryMapper.insertResultColumnBatch(queryEntity.getQueryResultConfig(), queryEntity.getQueryId());
            }

        }


    }

    @Transactional
    @Override
    public void delete(String id) {

        queryMapper.delete(id);

        queryMapper.deleteParams(id);

        queryMapper.deleteColumns(id);

    }

    @Transactional
    @Override
    public void updateById(QueryEntity e) {

        queryMapper.updateById(e);

        String id = e.getQueryId();

        queryMapper.deleteParams(id);

        queryMapper.deleteColumns(id);

        if (e.getQuerParamsConfig() != null && !e.getQuerParamsConfig().isEmpty()) {

            queryMapper.insertParamsBatch(e.getQuerParamsConfig(), id);

        }

        if (e.getQueryResultConfig() != null && !e.getQueryResultConfig().isEmpty()) {

            queryMapper.insertResultColumnBatch(e.getQueryResultConfig(), id);
        }


    }

    @Override
    public Page<QueryEntity> selectList(Page page) {

        List<QueryEntity> result = queryMapper.selectList(page);


        return page;
    }

    @Override
    public QueryEntity getOne(String queryId) {


        QueryEntity queryEntity = queryMapper.selectOne(queryId);

        queryEntity.setQuerParamsConfig(queryMapper.selectParams(queryId));

        queryEntity.setQueryResultConfig(queryMapper.selectColumns(queryId));

        return queryEntity;
    }

    @Override
    public void castQueryParams(String datasourceId, List<QueryParam> params) {

        if (params != null && dataSourceService.avaiable(datasourceId)) {

            Gson gson = new Gson();

            params.forEach(param -> {

                if (param.getParamDataType() == ParamDataType.SINGLE_SELECT || param.getParamDataType() == ParamDataType.MULTIPLE_SELECT) {

                    if (param.getParamDatasourceType() == ParamDataSourceType.SQL) {

                        //设置当前线程数据源id
                        DataSourceContextHolder.setDataSourceId(datasourceId);

                        List<Map> result = queryMapper.selectDynamicSql(param.getParamDatasourceContent());

                        //将查询结果转换为json字符串并赋值回去
                        param.setParamDatasourceContent(gson.toJson(result));

                        //改变一下数据源类型
                        param.setParamDatasourceType(ParamDataSourceType.JSON);

                        DataSourceContextHolder.clearId();

                    }
                }

            });
        }

    }

    @Override
    public List<Map> dynamicQuerySQL(String datasourceId, String sql, Map<String, String> params) {

        DataSourceContextHolder.setDataSourceId(datasourceId);

        //TODO 做一些参数检查，对多选进行处理

        if (params.containsKey("sql")) {
            throw new IllegalArgumentException("查询参数中不能含有sql的参数");
        }

        params.put("sql", sql);

        List<Map> result = queryMapper.dynamicSelect(params);

        DataSourceContextHolder.clearId();

        return result;
    }

    @Override
    public List<Map> dynamicQueryURL(String url, String method, Map<String, String> params, Map<String, String> headers) throws Exception {

        if (url == null || method == null) {


            throw new IllegalArgumentException("参数错误");
        }

        if (!method.toLowerCase().equals("get") && !method.toLowerCase().equals("post")) {

            throw new IllegalArgumentException("不支持的方法:" + method);
        }

        CloseableHttpClient closeableHttpClient = HttpClients.createDefault();


        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(5000)   //设置连接超时时间
                .setConnectionRequestTimeout(5000) // 设置请求超时时间
                .setSocketTimeout(5000)
                .setRedirectsEnabled(true)//默认允许自动重定向
                .setAuthenticationEnabled(true)
                .build();


        final HttpRequestBase requestBase;


        List<NameValuePair> paramList = new ArrayList<>();
        if (params != null) {


            params.forEach((k, v) -> {

                paramList.add(new BasicNameValuePair(k, v));
            });

            if (method.toLowerCase().equals("get")) {


                URIBuilder uriBuilder = new URIBuilder(url);

                uriBuilder.setParameters(paramList);


                requestBase = new HttpGet(uriBuilder.build());


            } else {

                HttpPost post = new HttpPost(url);

                requestBase = post;

                post.setEntity(new UrlEncodedFormEntity(paramList, "UTF-8"));

            }

            if (requestBase != null) {

                requestBase.setConfig(requestConfig);

                if (headers != null) {
                    headers.forEach((k, v) -> {

                        requestBase.addHeader(k, v);
                    });
                }


                CloseableHttpResponse response = closeableHttpClient.execute(requestBase);


                int respCode = response.getStatusLine().getStatusCode();

                if (respCode >= 200 && respCode < 300) {

                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

                    response.getEntity().writeTo(byteArrayOutputStream);


                    Gson gson = new Gson();

                    List<Map> mapList = gson.fromJson(new String(byteArrayOutputStream.toByteArray()), new TypeToken<List<Map>>() {
                    }.getType());

                    log.info("请求url:{} 完毕，数据解析成功", url);

                    return mapList;

                }


            }

        }


        return Collections.EMPTY_LIST;
    }
}
